#include "Matrix2D.h"
#include <cmath>
using namespace SolidityEngine;

Matrix2D::Matrix2D():
    _a11(0.0f),
    _a12(0.0f),
    _a21(0.0f),
    _a22(0.0f)
{}

Matrix2D::~Matrix2D(){}

Matrix2D::Matrix2D(float e11,float e12, float e21, float e22):
    _a11(e11),
    _a12(e12),
    _a21(e21),
    _a22(e22)
{}

Matrix2D::Matrix2D(float a[4]):
    _a11(a[0]),
    _a12(a[1]),
    _a21(a[2]),
    _a22(a[3])
{}

Matrix2D::Matrix2D(const Vector2D& top, const Vector2D& bottom):
    _a11(top.X()),
    _a12(top.Y()),
    _a21(bottom.X()),
    _a22(bottom.Y())
{}

float& Matrix2D::Xx(){
    return _a11;
}

float Matrix2D::Xx() const{
    return _a11;
}

float& Matrix2D::Xy(){
    return _a12;
}

float Matrix2D::Xy() const{
    return _a12;
}

float& Matrix2D::Yx(){
    return _a21;
}

float Matrix2D::Yx() const{
    return _a21;
}

float& Matrix2D::Yy(){
    return _a22;
}

float Matrix2D::Yy() const{
    return _a22;
}

float Matrix2D::Det() const{
    return ((_a11*_a22)-(_a12*_a21));
}

Matrix2D Matrix2D::operator+(const Matrix2D& mat) const{
    Matrix2D matrix;
    matrix.Xx()=this->Xx()+mat.Xx();
    matrix.Xy()=this->Xy()+mat.Xy();
    matrix.Yx()=this->Yx()+mat.Yx();
    matrix.Yy()=this->Yy()+mat.Yy();
    return matrix;
}
Matrix2D Matrix2D::operator-(const Matrix2D& mat)const{
    Matrix2D matrix;
    matrix.Xx()=this->Xx()-mat.Xx();
    matrix.Xy()=this->Xy()-mat.Xy();
    matrix.Yx()=this->Yx()-mat.Yx();
    matrix.Yy()=this->Yy()-mat.Yy();
    return matrix;
}

Matrix2D Matrix2D::operator*(const Matrix2D& mat)const{
    Matrix2D matrix;
    matrix.Xx()=(this->Xx()*mat.Xx())+(this->Xy()*mat.Yx());
    matrix.Xy()=(this->Xx()*mat.Xy())+(this->Xy()*mat.Yy());
    matrix.Yx()=(this->Yx()*mat.Xx())+(this->Yy()*mat.Yx());
    matrix.Yy()=(this->Yx()*mat.Xy())+(this->Yy()*mat.Yy());
    return matrix;
}
Vector2D Matrix2D::operator*(const Vector2D& vec)const{
    Vector2D vector;
    vector.X()=(this->Xx()*vec.X())+(this->Xy()*vec.Y());
    vector.Y()=(this->Yx()*vec.X())+(this->Yy()*vec.Y());
    return vector;
}
Matrix2D Matrix2D::operator*(float val)const{
    Matrix2D matrix;
    matrix.Xx()=this->Xx()*val;
    matrix.Xy()=this->Xy()*val;
    matrix.Yx()=this->Yx()*val;
    matrix.Yy()=this->Yy()*val;
    return matrix;
}

bool Matrix2D::operator!=(const Matrix2D& rhs) const{
    if(*this==rhs){
        return false;
    }
    return true;
}

bool Matrix2D::operator==(const Matrix2D& rhs) const{
    if((this->_a11==rhs._a11) && (this->_a12==rhs._a12) &&(this->_a21==rhs._a21)&&(this->_a22==rhs._a22)){
        return true;
    }
    return false;
}



Matrix2D Matrix2D::Identity() {
    Matrix2D matrix(1.0f,0.0f,0.0f,1.0f);
    return matrix;
}

Matrix2D Matrix2D::Rotation(float angle){
    Matrix2D matrix(cos(angle),-sin(angle),sin(angle),cos(angle));
    return matrix;
}

Matrix2D Matrix2D::Normalized() const{
    float determinant=this->Det();
    Matrix2D matrix=Matrix2D(*this)*(1.0f/determinant);
    return matrix;
}

Matrix2D Matrix2D::Inverse() const{
    float determinant=this->Det();
    Matrix2D swapped_matrix(this->Yy(),this->Xy()*-1.0f,this->Yx()*-1.0f,this->Xx());
    return swapped_matrix*(1.0f/determinant);
}

void Matrix2D::Invert(){
    float determinant=this->Det();
    //matrix with swapped main diagonal
    float tempXx=this->Xx();
    this->Xx()=(this->Yy())/determinant;
    this->Yy()=(tempXx)/determinant;
    this->Xy()*=(-1.0f)/determinant;
    this->Yx()*=(-1.0f)/determinant;
}

void Matrix2D::Normalize(){
    float determinant=this->Det();
    this->Xx()=this->Xx()*(1.0f/determinant);
    this->Xy()=this->Xy()*(1.0f/determinant);
    this->Yx()=this->Yx()*(1.0f/determinant);
    this->Yy()=this->Yy()*(1.0f/determinant);
}
